﻿using MessagePack;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Linq;
using CSRedis;

namespace IOP.Extension.Cache
{
    public class RedisCacheService : ICacheService
    {

        /// <summary>
        /// 日志
        /// </summary>
        private readonly ILogger _Logger;

        /// <summary>
        /// 构造函数
        /// </summary>
        public RedisCacheService(IOptions<CSRedisCacheOptions> options, ILogger<RedisCacheService> logger)
        {
            CSRedisClient redis = null;
            if (options.Value.ConnectionStrings == null) throw new ArgumentNullException("ConnectionStrings");
            if (options.Value.ConnectionStrings.Length == 1) redis = new CSRedisClient(options.Value.ConnectionStrings[0]);
            else redis = new CSRedisClient(null, options.Value.ConnectionStrings);
            _Logger = logger;
            RedisHelper.Initialization(redis);
            MessagePackSerializer.DefaultOptions.WithResolver(options.Value.Resolver);
        }

        /// <summary>
        /// 判断是否存在
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public bool Exists(string key)
        {
            if (string.IsNullOrEmpty(key)) throw new ArgumentNullException(nameof(key));
            return RedisHelper.Exists(key);
        }

        /// <summary>
        /// 获取值
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public object Get(string key)
        {
            try
            {
                if (!RedisHelper.Exists(key)) return null;
                var value = RedisHelper.Get<byte[]>(key);
                return MessagePackSerializer.Deserialize<object>(value);
            }
            catch (Exception e)
            {
                _Logger.LogError(e.Message + e.StackTrace);
                throw e;
            }
        }
        /// <summary>
        /// 获取值
        /// </summary>
        /// <typeparam name="TItem"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public TItem Get<TItem>(string key)
        {
            try
            {
                if (!RedisHelper.Exists(key)) return default(TItem);
                var value = RedisHelper.Get<byte[]>(key);
                return MessagePackSerializer.Deserialize<TItem>(value);
            }
            catch (Exception e)
            {
                _Logger.LogError(e.Message + e.StackTrace);
                throw e;
            }
        }
        /// <summary>
        /// 尝试获取值
        /// </summary>
        /// <typeparam name="TItem"></typeparam>
        /// <param name="key"></param>
        /// <param name="item"></param>
        public void TryGetValue<TItem>(string key, out TItem item)
        {
            try
            {
                var value = RedisHelper.Get<byte[]>(key);
                if (value != null) item = MessagePackSerializer.Deserialize<TItem>(value);
                else item = default(TItem);
            }
            catch (Exception e)
            {
                _Logger.LogError(e.Message + e.StackTrace);
                item = default(TItem);
            }
        }
        /// <summary>
        /// 获取多个键值数据
        /// </summary>
        /// <param name="keys"></param>
        /// <returns></returns>
        public IDictionary<string, object> GetRange(IEnumerable<string> keys)
        {
            try
            {
                var dict = new Dictionary<string, object>();
                keys.ToList().ForEach(item => dict.Add(item, Get(item)));
                return dict;
            }
            catch (Exception e)
            {
                _Logger.LogError(e.Message + e.StackTrace);
                throw e;
            }
        }

        /// <summary>
        /// 移除键值对
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public bool Remove(string key)
        {
            try
            {
                RedisHelper.Del(key);
                return true;
            }
            catch (Exception e)
            {
                _Logger.LogError(e.Message + e.StackTrace);
                throw e;
            }
        }
        /// <summary>
        /// 移除多个键值对
        /// </summary>
        /// <param name="keys"></param>
        public void RemoveRange(IEnumerable<string> keys)
        {
            try
            {
                RedisHelper.Del(keys.ToArray());
            }
            catch (Exception e)
            {

                _Logger.LogError(e.Message + e.StackTrace);
                throw e;
            }
        }

        /// <summary>
        /// 设置值
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public bool Set(string key, object value)
        {
            try
            {
                var bin = MessagePackSerializer.Serialize(value);
                return RedisHelper.Set(key, bin);
            }
            catch (Exception e)
            {
                _Logger.LogError(e.Message + e.StackTrace);
                throw e;
            }
        }
        /// <summary>
        /// 设置值
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="absoluteExpiration"></param>
        /// <returns></returns>
        public bool Set(string key, object value, DateTimeOffset absoluteExpiration)
        {
            try
            {
                var bin = MessagePackSerializer.Serialize(value);
                return RedisHelper.Set(key, bin, (absoluteExpiration - DateTime.Now).Seconds);
            }
            catch (Exception e)
            {
                _Logger.LogError(e.Message + e.StackTrace);
                throw e;
            }
        }
        /// <summary>
        /// 设置值
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="expiresIn"></param>
        /// <returns></returns>
        public bool Set(string key, object value, TimeSpan expiresIn)
        {
            try
            {
                var bin = MessagePackSerializer.Serialize(value);
                return RedisHelper.Set(key, bin, expiresIn.Seconds);
            }
            catch (Exception e)
            {
                _Logger.LogError(e.Message + e.StackTrace);
                throw e;
            }
        }
        /// <summary>
        /// 设置值
        /// </summary>
        /// <typeparam name="TItem"></typeparam>
        /// <param name="key"></param>
        /// <param name="item"></param>
        /// <returns></returns>
        public bool Set<TItem>(string key, TItem item) where TItem : class
        {
            try
            {
                var bin = MessagePackSerializer.Serialize(item);
                return RedisHelper.Set(key, bin);
            }
            catch (Exception e)
            {
                _Logger.LogError(e.Message + e.StackTrace);
                throw e;
            }
        }
        /// <summary>
        /// 设置值
        /// </summary>
        /// <typeparam name="TItem"></typeparam>
        /// <param name="key"></param>
        /// <param name="item"></param>
        /// <param name="absoluteExpiration"></param>
        /// <returns></returns>
        public bool Set<TItem>(string key, TItem item, DateTimeOffset absoluteExpiration) where TItem : class
        {
            try
            {
                var bin = MessagePackSerializer.Serialize(item);
                return RedisHelper.Set(key, bin, (absoluteExpiration - DateTime.Now).Seconds);
            }
            catch (Exception e)
            {
                _Logger.LogError(e.Message + e.StackTrace);
                throw e;
            }
        }
        /// <summary>
        /// 设置值
        /// </summary>
        /// <typeparam name="TItem"></typeparam>
        /// <param name="key"></param>
        /// <param name="item"></param>
        /// <param name="expiresIn"></param>
        /// <returns></returns>
        public bool Set<TItem>(string key, TItem item, TimeSpan expiresIn) where TItem : class
        {
            try
            {
                var bin = MessagePackSerializer.Serialize(item);
                return RedisHelper.Set(key, bin, expiresIn.Seconds);
            }
            catch (Exception e)
            {
                _Logger.LogError(e.Message + e.StackTrace);
                throw e;
            }
        }
    }
}
